/*
 * KKScene.cpp
 *
 *  Created on: 2013-9-15
 *      Author: wanghua
 */


#include "KKScene.h"
#include "KKDirector.h"
#include "XmlUtils.h"
#include "KKContactListener.h"
#include <iostream>

float KKScene::PTM_RATIO = 30;

using namespace std;

KKScene::KKScene(const char* sceneName, const char* sceneFile) {
	m_name = sceneName;
	m_path = sceneFile;
	m_storyboards = new KKStoryboardDictionary();
	m_sceneUpdateListener = NULL;
	this->m_isLoaded = false;
	this->m_push = false;
}

KKScene::~KKScene() {
	CC_SAFE_DELETE(m_sceneUpdateListener);
	CC_SAFE_DELETE(m_world);
}

void KKScene::onEnter() {
	CCScene::onEnter();
	if (!this->m_isLoaded) {
		this->loadScene();
	}
}

void KKScene::onExit() {
	CCScene::onExit();

	if (this->m_isLoaded) {

		CCLog("%s exit with retainCount=%d \n", m_name.c_str(),
				this->retainCount());

		if (!m_push) {
			this->unloadScene();
		}

	}
}

KKScene* KKScene::sceneWithFile(const char* sceneName, const char* sceneFile) {

	KKScene* scene = new KKScene(sceneName, sceneFile);

	scene->autorelease();

	return scene;
}

void KKScene::resetNode(bool reload, bool stopAction) {

	if (reload) {
		reloadScene();
	} else {
		KKNode* child = NULL;
		for (unsigned int i = 0; i < m_objects->count(); i++) {
			child = m_objects->getObjectAtIndex(i);
			if (child) {
				child->resetNode(reload, stopAction);
			}
		}

		layout();
	}

}

void KKScene::loadNode() {
	loadScene();
}

void KKScene::reloadScene() {
	this->retain();
	this->unloadScene();
	this->loadScene();
	this->release();
}

bool KKScene::loadScene() {
	CCLog("load scene %s \n", this->m_name.c_str());

	bool result = loadScene(this->m_path.c_str());

	if (result) {
		initWorld();
		loadStoryboards((this->m_path + ".anim").c_str());
		this->onInit();
	}

	this->m_isLoaded = true;

	return result;
}

void KKScene::unloadScene() {

	CCLog("unload scene %s \n", this->m_name.c_str());

	if (KKDirector::isDebug) {
		CCTextureCache::sharedTextureCache()->dumpCachedTextureInfo();
	}

	m_storyboards->removeAllObjects();

	this->m_isLoaded = false;

	this->removeAllNodes(true);

	destoryWorld();

	if (KKDirector::isDebug) {
		CCLog("after unload scene %s\n", this->m_name.c_str());

		CCTextureCache::sharedTextureCache()->dumpCachedTextureInfo();
	}

	CC_SAFE_DELETE(this->m_document);
	this->m_element = NULL;
}

bool KKScene::loadScene(const char* mapFile) {

	CCLog("KAKA initWithMapFile %s \n", mapFile);

	TiXmlDocument *myDocument = XmlUtils::readXML(mapFile);

	CCLog("KAKA loaded MapFile \n");

	TiXmlElement *rootElement = myDocument->RootElement();

	//CCFileUtils::setResourcePath(rootElement->Attribute("res"));

	CCSize winSize = CCDirector::sharedDirector()->getWinSize();

	this->m_width = winSize.width;//atoi(rootElement->Attribute("width"));
	this->m_height = winSize.height;//atoi(rootElement->Attribute("height"));

	this->m_ccNode = this;

	TiXmlElement * layoutElement = rootElement->FirstChildElement("layout");

	if (layoutElement) {
		this->m_layout = KKLayoutParser::parseFromXml(layoutElement);
	}

	TiXmlElement * childElement = rootElement->FirstChildElement("layer");

	while (childElement != NULL) {

		KKNode * layer = KKNodeParser::parseFromXml(childElement, NULL);

		if (layer) {
			CCObject* object = dynamic_cast<CCObject*> (layer);

			this->addNode(layer, layer->getZOrder());
		}

		childElement = childElement->NextSiblingElement("layer");
	}

	measure();
	layout();

	this->m_element = rootElement;
	this->m_document = myDocument;

	return true;
}

void KKScene::loadStoryboards(const char* storyboardFile) {

	TiXmlDocument *myDocument = XmlUtils::readXML(storyboardFile);

	TiXmlElement *rootElement = myDocument->RootElement();

	TiXmlElement * storyboardElement = rootElement->FirstChildElement(
			"storyboard");

	int index = 1;

	while (storyboardElement != NULL) {

		KKStoryboard* storyboard = KKStoryboard::storyboardWithXml(
				storyboardElement, index);

		if (storyboard) {
			index++;
			this->m_storyboards->setObject(storyboard, storyboard->getName());
		}

		storyboardElement = storyboardElement->NextSiblingElement("storyboard");
	}
}

KKStoryboard* KKScene::storyboardNamed(string storyboardName) {

	return this->m_storyboards->objectForKey(storyboardName);
}

void KKScene::initWorld() {

	b2Vec2 gravity;
	gravity.Set(0.0f, -10.0f);

	m_world = new b2World(gravity);
	m_world->SetAllowSleeping(true);
	m_world->SetContinuousPhysics(true);
	m_world->SetContactListener(KKContactListener::sharedListener());
	m_debugDraw = new GLESDebugDraw(PTM_RATIO);
	m_debugDraw->SetFlags(b2Draw::e_shapeBit | b2Draw::e_jointBit/* | b2Draw::e_aabbBit
	 | b2Draw::e_pairBit | b2Draw::e_centerOfMassBit*/);

	m_world->SetDebugDraw(m_debugDraw);

	scheduleUpdate();

}

void KKScene::destoryWorld() {

	this->unscheduleUpdate();

	CC_SAFE_DELETE(m_world);

}

b2Body* KKScene::createBody(KKNode* sprite, int16 category, int16 mask,
		b2Shape* shape, string nodeVertexesName) {

	CCNode* node = sprite->convertToCCNode();

	b2BodyDef bodyDef;

	bodyDef.type = sprite->getBox2dPhysicsType();

	CCPoint point = node->getParent()->convertToWorldSpace(node->getPosition());

	//point = node->getPosition();

	bodyDef.position.Set(point.x / PTM_RATIO, point.y / PTM_RATIO);
	bodyDef.userData = node;

	bodyDef.angle = -CC_DEGREES_TO_RADIANS(node->getRotation());

	b2Body *body = m_world->CreateBody(&bodyDef);

	sprite->setBody(body);

	b2Fixture* fixture = NULL;

	if (!shape) {

		b2PolygonShape dynamicBox;

		KKNodeVertexesArray* nodeVertexesArray = sprite->getNodeVertexs();

		if (nodeVertexesArray) {
			KKNodeVertexesArray::CCMutableArrayIterator it =
					nodeVertexesArray->begin();

			while (it != nodeVertexesArray->end()) {
				if (nodeVertexesName != "") {
					KKNodeVertexes* knv = *it;
					if (nodeVertexesName != knv->getName().substr(0, 1)) {
						it++;
						continue;
					}
				}
				b2Fixture* fixtureSome = createFixture(sprite, *it, category,
						mask);

				if (fixture == NULL && fixtureSome) {
					fixture = fixtureSome;
				}

				it++;
			}

		}

		if (fixture == NULL) {

			b2PolygonShape dynamicBox;

			dynamicBox.SetAsBox(
					node->getContentSize().width * node->getScaleX() / 2
							/ PTM_RATIO,
					node->getContentSize().height * node->getScaleY() / 2
							/ PTM_RATIO);

			fixture = createFixture(sprite, &dynamicBox, category, mask);
		}

	} else {
		fixture = createFixture(sprite, shape, category, mask);
	}

	b2Vec2 velocity = b2Vec2(sprite->getVelocityX() / PTM_RATIO,
			sprite->getVelocityY() / PTM_RATIO);

	body->SetLinearVelocity(velocity);

	return body;
}

b2Fixture* KKScene::createFixture(KKNode* sprite, KKNodeVertexes* nodeVertexes,
		int16 category, int16 mask) {

	int vertexCount = int(nodeVertexes->getVertexs()->count());

	b2Vec2* vertexes = new b2Vec2[vertexCount];

	KKPoint* vertex = NULL;

	float scaleX = sprite->getScaledWidth() / sprite->getWidth();
	float scaleY = sprite->getScaledHeight() / sprite->getHeight();

	for (int i = 0; i < vertexCount; i++) {
		vertex = nodeVertexes->getVertexs()->getObjectAtIndex(i);
		vertexes[i] = b2Vec2(vertex->x * scaleX / PTM_RATIO,
				vertex->y * scaleY / PTM_RATIO);
	}

	if (nodeVertexes->getType() == "poly") {

		if (vertexCount > 2) {

			if (vertexCount > b2_maxPolygonVertices) {

				int restCount = vertexCount % b2_maxPolygonVertices;

				if (restCount < 3) {

					for (int i = 0; i < 3 - restCount; i++) {
						vertexes[vertexCount] = vertexes[i];
						vertexCount++;
					}
				} else {
					vertexes[vertexCount] = vertexes[0];
					vertexCount++;
				}
			}

			b2PolygonShape dynamicBox;

			int vertexIndex = 0;

			b2Fixture* fixture = NULL;

			while (vertexIndex < vertexCount) {

				int count = min(vertexCount - vertexIndex,
						b2_maxPolygonVertices);

				CCLog("vertexIndex=%d,cont=%d\n", vertexIndex, count);

				if (count > 2) {
					dynamicBox.Set(vertexes + vertexIndex, count);

					fixture
							= createFixture(sprite, &dynamicBox, category, mask);
				}

				vertexIndex += b2_maxPolygonVertices - 1;
			}

			CC_SAFE_DELETE_ARRAY(vertexes);
			return fixture;

		}

	} else if (nodeVertexes->getType() == "circle") {

		if (vertexCount > 1) {

			b2CircleShape circleShape;

			KKPoint* center = nodeVertexes->getVertexs()->getObjectAtIndex(0);
			KKPoint* edge = nodeVertexes->getVertexs()->getObjectAtIndex(1);
			circleShape.m_radius = ccpDistance(ccp(center->x, center->y),
					ccp(edge->x, edge->y)) / PTM_RATIO;

			circleShape.m_p.Set(center->x / PTM_RATIO, center->y / PTM_RATIO);

			CC_SAFE_DELETE_ARRAY(vertexes);
			return createFixture(sprite, &circleShape, category, mask);
		}
	} else if (nodeVertexes->getType() == "line") {

		b2ChainShape edgeShape;

		edgeShape.CreateChain(vertexes, vertexCount);

		CC_SAFE_DELETE_ARRAY(vertexes);
		return createFixture(sprite, &edgeShape, category, mask);
	}

	CC_SAFE_DELETE_ARRAY(vertexes);

	return NULL;
}

b2Fixture* KKScene::createFixture(KKNode* sprite, b2Shape* shape,
		int16 category, int16 mask) {

	b2FixtureDef fixtureDef;

	fixtureDef.shape = shape;

	fixtureDef.density = sprite->getDensity();
	fixtureDef.friction = sprite->getFriction();
	fixtureDef.restitution = sprite->getRestitution();

	//fixtureDef.filter.groupIndex = group;
	fixtureDef.filter.categoryBits = category;
	fixtureDef.filter.maskBits = mask;
	fixtureDef.isSensor = sprite->getIsSensor();

	return sprite->getBody()->CreateFixture(&fixtureDef);
}
void KKScene::destroyBody(KKNode* sprite) {
	if (m_world->GetBodyCount() > 0) {
		m_world->DestroyBody(sprite->getBody());
	}

}

void KKScene::update(ccTime dt) {

	int velocityIterations = 8;
	int positionIterations = 1;

	m_world->Step(dt, velocityIterations, positionIterations);

	//Iterate over the bodies in the physics world
	for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext()) {
		if (b->GetUserData() != NULL && b->GetType() == b2_dynamicBody) {
			//Synchronize the AtlasSprites position and rotation with the corresponding body
			CCNode* myActor = (CCNode*) (b->GetUserData());

			/*CCLog("x=%f,x1=%f\n",myActor->getParent()->convertToNodeSpace(CCPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO)).x,CCPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO).x);

			 myActor->setPosition(myActor->getParent()->convertToNodeSpace(
			 CCPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO)));*/

			b->SetTransform(b->GetPosition(), 0);

			myActor->setPosition(
					CCPointMake(b->GetPosition().x * PTM_RATIO,
							b->GetPosition().y * PTM_RATIO));

			myActor->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));
		}
	}

	if (m_sceneUpdateListener) {
		m_sceneUpdateListener->onUpdate(dt);
	}

}

void KKScene::visit() {

	CCScene::visit();

	if (KKDirector::isDebug) {

		glDisable( GL_TEXTURE_2D);
		glDisableClientState( GL_COLOR_ARRAY);
		glDisableClientState( GL_TEXTURE_COORD_ARRAY);

		m_world->DrawDebugData();

		glEnable(GL_TEXTURE_2D);

		glEnableClientState(GL_COLOR_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	}

}
